%% @doc The trader module keeps track of all usable harbors and given a resource hand it calculate all possible hands with the use of trades 
%% @end 
-module(trader).
-export([init/0,getTrades/3,makeTrade/3,trader/3]).

%We have no harbors from the start
%% @doc starts the trader as a new process
%% @spec init() -> PID
%% @end
init() -> spawn(fun() -> trader(false,[], []) end).

%The main function of the trader agent
%% @doc  The main function of the trader agent, keeps track of usable harbors and makes trades with the bank.  
%% @end
trader(ThreeOne,TwoOnes, IDs) -> 
    receive
		{initIDs, NewIDs} -> trader(ThreeOne,TwoOnes, NewIDs);
        {update, Harbor} -> 
            case Harbor of
                threeOne -> trader(true,TwoOnes, IDs);
                Harbor   -> trader(ThreeOne,lists:sort([Harbor|TwoOnes]), IDs) %viktig att TwoOnes är sorterad 
           end;
        {trades, Cards, Pid} ->
            Trades = getTrades(Cards,ThreeOne,TwoOnes),
            Pid!{trades, Trades},
            trader(ThreeOne,TwoOnes,IDs);
		{makeTrade,Res,NRes} -> {parse_message, PMPid} = lists:keyfind(parse_message,1,IDs),
			makeTrade(Res, NRes, PMPid),
			trader(ThreeOne,TwoOnes,IDs)
    end.


%Returns a list of all hands possible to get from the given hand
%Saknas att dra med vilka byten som ska göras med varje hand
%viktigt att Hamnarna ligger i samma ordning som Res
%% @doc  Given a hand of resources getTrades calculates all possible hands with the use of trade with the bank   
%% @spec getTrades([{atomic,Integer}],boolean,[{atomic,Integer}]) -> [[{atomic,Integer}]]
%% @end
getTrades(Cards,ThreeOne,TwoOnes) ->
	C1 = getHands(Cards,ThreeOne,TwoOnes),
	C2 = [C1] ++ lists:map(fun(X) -> getHands(X,ThreeOne,TwoOnes) end, C1),
	C3 = lists:concat(C2),
	C4 = sets:to_list(sets:from_list(C3)),
    if
       C4 == [] -> [Cards];
       true  -> C4 ++ [Cards]
    end.

getHands(Cards,ThreeOne,TwoOnes) -> getHands(Cards,Cards,ThreeOne,TwoOnes,0).
getHands([],_,_,_,_) ->	[];
getHands([{Res,Nr}|Cards],Hand,ThreeOne,TwoOnes, Inc) ->
	if
		(TwoOnes /= []) and (hd(TwoOnes) == Res) -> PTrade = Nr div 2, Sub = 2, Huse = true;
		ThreeOne -> PTrade = Nr div 3, Sub = 3, Huse = false;
		true     -> PTrade = Nr div 4, Sub = 4, Huse = false
	end,
    if 
		PTrade > 0 -> NHand = lists:keyreplace(Res,1,Hand, {Res,Nr - Sub}),
					  newResList(NHand,NHand,Inc+1,Res) ++ getHands([{Res,Nr -Sub}|Cards],Hand,ThreeOne,TwoOnes, Inc+1);
        Huse       -> getHands(Cards,Hand,ThreeOne,tl(TwoOnes),0);
		true       -> getHands(Cards,Hand,ThreeOne,TwoOnes,0)
	end.

newResList([],_,_,_) -> [];
newResList([{Res,_  }|Cards],L,Inc,Res) -> newResList(Cards,L,Inc,Res);
newResList([{Res,RNr}|Cards],L,Inc,NRes) -> [lists:keyreplace(Res,1,L,{Res ,RNr+Inc})] ++ newResList(Cards,L,Inc,NRes).

%% @doc  Calculates what resources to get and what to trade away, given two resource list. The result is sent to PMPid, msg:parse_message
%% @spec makeTrade([{atomic,Integer}],[{atomic,Integer}],PID) -> ok
%% @end
makeTrade(Res,NewRes,PMPid) -> DiffList = lists:map(fun({{_,Ra},{_,Na}}) -> Ra - Na end,lists:zip(Res,NewRes)),
					    Give = lists:map(fun(Diff)-> if Diff > 0 -> Diff; true -> 0 end end, DiffList),
						Get  = lists:map(fun(Diff)-> if Diff < 0 -> -Diff; true -> 0 end end, DiffList),
						Sum = lists:sum(Give),
						if
							Sum == 0 -> false;
							true -> PMPid ! {banktrade,Give,Get}
						end.
